﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace ZhCun.Utils.Helpers
{
    /// <summary>
    /// 有相关操作系统Api的封装类
    /// </summary>
    public class SysApiHelper
    {
        #region 设置系统时间

        [StructLayoutAttribute(LayoutKind.Sequential)]
        class SystemTime
        {
            public ushort vYear;
            public ushort vMonth;
            public ushort vDayOfWeek;
            public ushort vDay;
            public ushort vHour;
            public ushort vMinute;
            public ushort vSecond;
        }

        [DllImportAttribute("Kernel32.dll")]
        static extern void GetLocalTime(SystemTime st);

        [DllImportAttribute("Kernel32.dll")]
        static extern void SetLocalTime(SystemTime st);
        /// <summary>
        /// 通过api获取本地时间
        /// </summary>
        /// <returns>返回c#时间类型</returns>
        public static DateTime GetLocalTime()
        {
            SystemTime sysTime = new SystemTime();
            GetLocalTime(sysTime);
            DateTime dt = new DateTime(sysTime.vYear, sysTime.vMonth, sysTime.vDay, sysTime.vHour, sysTime.vMinute, sysTime.vSecond);
            return dt;
        }
        /// <summary>
        /// 根据c#时间类型设置本地时间
        /// </summary>
        public static bool SetLocalTime(DateTime dt)
        {
            SystemTime sysTime = new SystemTime();
            sysTime.vYear = (ushort)dt.Year;
            sysTime.vMonth = (ushort)dt.Month;
            sysTime.vDay = (ushort)dt.Day;
            sysTime.vHour = (ushort)dt.Hour;
            sysTime.vMinute = (ushort)dt.Minute;
            sysTime.vSecond = (ushort)dt.Second;
            SetLocalTime(sysTime);
            return true;
        }

        #endregion

        #region 查找窗口

        [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        /// <summary>
        /// 查找指定句柄下的window句柄,可制定class或标题
        /// </summary>
        /// <param name="hwndParent"></param>
        /// <param name="hwndChildAfter"></param>
        /// <param name="lpszClass"></param>
        /// <param name="lpszWindow"></param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true)]
        static extern IntPtr FindWindowEx(IntPtr hwndParent, uint hwndChildAfter, string lpszClass, string lpszWindow);

        delegate bool EnumChildCallBack(IntPtr hwnd, int lParam);

        [DllImport("user32.dll")]
        static extern int EnumChildWindows(IntPtr hWndParent, EnumChildCallBack lpfn, int lParam);

        /// <summary>
        /// 根据标题查找window句柄
        /// </summary>
        public static IntPtr FindWindowByTitle(string title)
        {
            IntPtr ip = FindWindow(null, title);
            return ip;
        }

        /// <summary>
        /// 遍历查找指定句柄窗口下的包含classname的window（或控件）的句柄，仅返回第一个
        /// </summary>
        public static IntPtr FindWindowExByClass(IntPtr hwnd, string className, string lpszWindow = null)
        {
            IntPtr iResult = IntPtr.Zero;
            // 首先在父窗体上查找控件
            iResult = FindWindowEx(hwnd, 0, className, lpszWindow);
            // 如果找到直接返回控件句柄
            if (iResult != IntPtr.Zero) return iResult;
            // 枚举子窗体，查找控件句柄
            int i = EnumChildWindows(
            hwnd,
            (h, l) =>
            {
                IntPtr f1 = FindWindowEx(h, 0, className, null);
                if (f1 == IntPtr.Zero)
                    return true;
                else
                {
                    iResult = f1;
                    return false;
                }
            },
            0);
            // 返回查找结果
            return iResult;
        }

        #endregion

        #region 获取窗口位置

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;                             //最左坐标
            public int Top;                             //最上坐标
            public int Right;                           //最右坐标
            public int Bottom;                        //最下坐标
        }

        #endregion

        #region 进程、线程、内存等操作

        const int PROCESS_VM_OPERATION = 0x0008;
        const int PROCESS_VM_READ = 0x0010;
        const int PROCESS_VM_WRITE = 0x0020;

        public const int MEM_COMMIT = 0x1000;
        public const int MEM_RELEASE = 0x8000;
        public const int MEM_RESERVE = 0x2000;

        public const int PAGE_READWRITE = 4;

        [DllImport("user32.dll")]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint dwProcessId);

        [DllImport("user32.dll")]
        public static extern int GetThreadId(IntPtr hwnd);

        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

        [DllImport("kernel32.dll")]
        public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
            uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress,
           uint dwSize, uint dwFreeType);

        [DllImport("kernel32.dll")]
        public static extern bool CloseHandle(IntPtr handle);

        [DllImport("kernel32.dll")]
        public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
           IntPtr lpBuffer, int nSize, ref uint vNumberOfBytesRead);

        [DllImport("kernel32.dll")]
        public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
           IntPtr lpBuffer, int nSize, ref uint vNumberOfBytesRead);

        #endregion

        #region 获取ListView数据项

        const int LVM_FIRST = 0x1000;
        const int LVM_GETITEMCOUNT = LVM_FIRST + 4;
        const int LVM_GETITEMW = LVM_FIRST + 75;
        const int LVM_GETHEADER = (LVM_FIRST + 31);

        const int LVIF_TEXT = 0x0001;

        const int HDM_FIRST = 0x1200;
        const int HDM_GETITEMCOUNT = (HDM_FIRST + 0);

        struct LVITEM
        {
            public int mask;
            public int iItem;
            public int iSubItem;
            public int state;
            public int stateMask;
            public IntPtr pszText; // string 
            public int cchTextMax;
            public int iImage;
            public IntPtr lParam;
            public int iIndent;
            public int iGroupId;
            public int cColumns;
            public IntPtr puColumns;
        }

        /// <summary>
        /// 获取window空间listview项数据
        /// </summary>
        public static string[][] GetListView(IntPtr vHandle)
        {
            //获取到列头的句柄
            int headPtr = SendMessage(vHandle, LVM_GETHEADER, 0, 0);
            //获取列数量
            int colCount = SendMessage(new IntPtr(headPtr), HDM_GETITEMCOUNT, 0, 0);
            int rowCount = SendMessage(vHandle, LVM_GETITEMCOUNT, 0, 0);

            string[][] rData = new string[rowCount][];

            uint vProcessId;
            uint threadId = GetWindowThreadProcessId(vHandle, out vProcessId);

            IntPtr vProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ |
                PROCESS_VM_WRITE, false, vProcessId);
            IntPtr vPointer = VirtualAllocEx(vProcess, IntPtr.Zero, 4096,
                MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
            try
            {
                for (int i = 0; i < rowCount; i++)
                {
                    rData[i] = new string[colCount];
                    for (int j = 0; j < colCount; j++)
                    {
                        byte[] vBuffer = new byte[256];
                        LVITEM[] vItem = new LVITEM[1];
                        vItem[0].mask = LVIF_TEXT;
                        vItem[0].iItem = i;
                        vItem[0].iSubItem = j;
                        vItem[0].cchTextMax = vBuffer.Length;
                        vItem[0].pszText = (IntPtr)((int)vPointer + Marshal.SizeOf(typeof(LVITEM)));
                        uint vNumberOfBytesRead = 0;

                        WriteProcessMemory(vProcess, vPointer,
                            Marshal.UnsafeAddrOfPinnedArrayElement(vItem, 0),
                            Marshal.SizeOf(typeof(LVITEM)),
                            ref vNumberOfBytesRead);
                        SendMessage(vHandle, LVM_GETITEMW, i, vPointer.ToInt32());
                        ReadProcessMemory(vProcess,
                            (IntPtr)((int)vPointer + Marshal.SizeOf(typeof(LVITEM))),
                            Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0),
                            vBuffer.Length, ref vNumberOfBytesRead);
                        string vText = Marshal.PtrToStringUni(
                            Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0));
                        rData[i][j] = vText;
                    }
                }
            }
            finally
            {
                VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
                CloseHandle(vProcess);
            }
            return rData;
        }

        #endregion

        #region sendMessage


        [DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr hwnd, uint wMsg, int wParam, int lParam);

        [DllImport("user32.dll", EntryPoint = "SendMessageA", SetLastError = true, CharSet = CharSet.Auto)]
        static extern int SendMessageA(IntPtr hwnd, int wMsg, int wParam, StringBuilder lParam);

        #endregion

        #region 获取或设置文本内容

        const int WM_GETTEXT = 0x000D;
        const int WM_SETTEXT = 0x000C;

        /// <summary>
        /// 根据句柄获取文本内容
        /// </summary>
        public static string GetWindowText(IntPtr hwnd)
        {
            int buffer_size = 1024;
            StringBuilder buffer = new StringBuilder(buffer_size);
            int r = SendMessageA(hwnd, WM_GETTEXT, buffer_size, buffer);
            return buffer.ToString();
        }
        /// <summary>
        /// 根据句柄设置文本内容
        /// </summary>
        public static void SetWindowText(IntPtr hwnd, string value)
        {
            StringBuilder buffer = new StringBuilder(value);
            int r = SendMessageA(hwnd, WM_SETTEXT, 0, buffer);
        }

        #endregion

        #region 钩子

        public delegate int GlobalHookProc(int nCode, Int32 wParam, IntPtr lParam);

        /// <summary>
        /// 装载钩子
        /// </summary>
        [DllImport("user32.dll", EntryPoint = "SetWindowsHookEx", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, GlobalHookProc lpfn, IntPtr hInstance, int threadId);

        /// <summary>
        /// 卸载钩子方法
        /// </summary>
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        /// <summary>
        /// 使用这个函数钩信息传递给链中的下一个钩子过程。
        /// </summary>
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        /// <summary>
        /// 设置的钩子类型
        /// </summary>
        public enum HookType : int
        {
            /// <summary>
            /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单，滚动 
            ///条，消息框，对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 
            ///WH_MSGFILTER Hook只能监视传递到菜单，滚动条，消息框的消息，以及传递到通 
            ///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook 
            ///监视所有应用程序消息。 
            /// 
            ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 
            ///过滤消息，这等价于在主消息循环中过滤消息。 
            ///    
            ///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 
            ///个函数，应用程序能够在模式循环期间使用相同的代码去过滤消息，如同在主消息循 
            ///环里一样
            /// </summary>
            WH_MSGFILTER = -1,
            /// <summary>
            /// WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的，可以使用这 
            ///个Hook记录连续的鼠标和键盘事件，然后通过使用WH_JOURNALPLAYBACK Hook 
            ///来回放。WH_JOURNALRECORD Hook是全局Hook，它不能象线程特定Hook一样 
            ///使用。WH_JOURNALRECORD是system-wide local hooks，它们不会被注射到任何行 
            ///程地址空间
            /// </summary>
            WH_JOURNALRECORD = 0,
            /// <summary>
            /// WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可 
            ///以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠 
            ///标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装，正常的鼠标和键盘 
            ///事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook，它不能象线程特定 
            ///Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值，这个值告诉系统在处 
            ///理来自回放Hook当前消息之前需要等待多长时间（毫秒）。这就使Hook可以控制实 
            ///时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks，它们不会被 
            ///注射到任何行程地址空间
            /// </summary>
            WH_JOURNALPLAYBACK = 1,
            /// <summary>
            /// 在应用程序中，WH_KEYBOARD Hook用来监视WM_KEYDOWN and  
            ///WM_KEYUP消息，这些消息通过GetMessage or PeekMessage function返回。可以使 
            ///用这个Hook来监视输入到消息队列中的键盘消息
            /// </summary>
            WH_KEYBOARD = 2,
            /// <summary>
            /// 应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函 
            ///数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入，以及 
            ///其它发送到消息队列中的消息
            /// </summary>
            WH_GETMESSAGE = 3,
            /// <summary>
            /// 监视发送到窗口过程的消息，系统在消息发送到接收窗口过程之前调用
            /// </summary>
            WH_CALLWNDPROC = 4,
            /// <summary>
            /// 在以下事件之前，系统都会调用WH_CBT Hook子过程，这些事件包括： 
            ///1. 激活，建立，销毁，最小化，最大化，移动，改变尺寸等窗口事件； 
            ///2. 完成系统指令； 
            ///3. 来自系统消息队列中的移动鼠标，键盘事件； 
            ///4. 设置输入焦点事件； 
            ///5. 同步系统消息队列事件。
            ///Hook子过程的返回值确定系统是否允许或者防止这些操作中的一个
            /// </summary>
            WH_CBT = 5,
            /// <summary>
            /// WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单，滚动 
            ///条，消息框，对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。 
            ///WH_MSGFILTER Hook只能监视传递到菜单，滚动条，消息框的消息，以及传递到通 
            ///过安装了Hook子过程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook 
            ///监视所有应用程序消息。 
            /// 
            ///WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间 
            ///过滤消息，这等价于在主消息循环中过滤消息。 
            ///    
            ///通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这 
            ///个函数，应用程序能够在模式循环期间使用相同的代码去过滤消息，如同在主消息循 
            ///环里一样
            /// </summary>
            WH_SYSMSGFILTER = 6,
            /// <summary>
            /// WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。 
            ///使用这个Hook监视输入到消息队列中的鼠标消息
            /// </summary>
            WH_MOUSE = 7,
            /// <summary>
            /// 当调用GetMessage 或 PeekMessage 来从消息队列种查询非鼠标、键盘消息时
            /// </summary>
            WH_HARDWARE = 8,
            /// <summary>
            /// 在系统调用系统中与其它Hook关联的Hook子过程之前，系统会调用 
            ///WH_DEBUG Hook子过程。你可以使用这个Hook来决定是否允许系统调用与其它 
            ///Hook关联的Hook子过程
            /// </summary>
            WH_DEBUG = 9,
            /// <summary>
            /// 外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是 
            ///激活的并且当顶层窗口建立或者销毁时，系统调用WH_SHELL Hook子过程。 
            ///WH_SHELL 共有５钟情况： 
            ///1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁； 
            ///2. 当Taskbar需要重画某个按钮； 
            ///3. 当系统需要显示关于Taskbar的一个程序的最小化形式； 
            ///4. 当目前的键盘布局状态改变； 
            ///5. 当使用者按Ctrl+Esc去执行Task Manager（或相同级别的程序）。 
            ///
            ///按照惯例，外壳应用程序都不接收WH_SHELL消息。所以，在应用程序能够接 
            ///收WH_SHELL消息之前，应用程序必须调用SystemParametersInfo function注册它自 
            ///己
            /// </summary>
            WH_SHELL = 10,
            /// <summary>
            /// 当应用程序的前台线程处于空闲状态时，可以使用WH_FOREGROUNDIDLE  
            ///Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时，系统就 
            ///会调用WH_FOREGROUNDIDLE Hook子过程
            /// </summary>
            WH_FOREGROUNDIDLE = 11,
            /// <summary>
            /// 监视发送到窗口过程的消息，系统在消息发送到接收窗口过程之后调用
            /// </summary>
            WH_CALLWNDPROCRET = 12,
            /// <summary>
            /// 监视输入到线程消息队列中的键盘消息
            /// </summary>
            WH_KEYBOARD_LL = 13,
            /// <summary>
            /// 监视输入到线程消息队列中的鼠标消息
            /// </summary>
            WH_MOUSE_LL = 14
        }

        public class HookHelper
        {
            public int _HookId;

            int GlobalHookProc(int nCode, Int32 wParam, IntPtr lParam)
            {

                return CallNextHookEx(_HookId, nCode, wParam, lParam);
            }

            // Create an instance of HookProc.
            public GlobalHookProc HookProcedure;

            /// <summary>
            /// 装在hook
            /// </summary>
            /// <param name="hwnd"></param>
            /// <param name="hookType"></param>
            /// <returns></returns>
            public int SetHook(IntPtr hwnd, HookType hookType)
            {
                HookProcedure = new GlobalHookProc(GlobalHookProc);
                _HookId = (int)SetWindowsHookEx((int)hookType, HookProcedure, hwnd, 0);                
                return _HookId;
            }
            public void UnHook()
            {
                bool r = UnhookWindowsHookEx(_HookId);
            }
            ~HookHelper()
            {
                UnHook();
            }
            public void Dispose()
            {
                UnHook();
            }
        }


        #endregion

        #region 响应按键

        [DllImport("user32.dll", SetLastError = true)]
        static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
        /// <summary>
        /// 响应指定按键
        /// </summary>
        /// <param name="key">取 System.Windows.Forms.Keys 枚举值</param>
        /// <param name="up">true：是响应松开,false：响应按下</param>
        public static void PressKey(byte key, bool up = false)
        {
            const int KEYEVENTF_EXTENDEDKEY = 0x1;
            const int KEYEVENTF_KEYUP = 0x2;
            if (up)
            {

                keybd_event(key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, (UIntPtr)0);

            }
            else
            {

                keybd_event(key, 0x45, KEYEVENTF_EXTENDEDKEY, (UIntPtr)0);

            }
        }

        #endregion
    }
}
